/*********************************************************************************
* Copyright 2019 Huawei Technologies Co.,Ltd.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License.  You may obtain a copy of the
* License at
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations under the License.
**********************************************************************************
*/

#include "securec.h"
#include "securecutil.h"

/*******************************************************************************
 * <NAME>
 *    strncat_s
 *
 * <SYNOPSIS>
 *    errno_t
 *    strncat_s(char* strDest, size_t destMax, const char* strSrc, size_t count);
 *
 * <FUNCTION DESCRIPTION>
 *    The strncat_s function try to append the first D characters of strSrc to 
 *    the end of strDest, where D is the lesser of count and the length of strSrc.
 *    If appending those D characters will fit within strDest (whose size is given
 *    as destMax) and still leave room for a null terminator, then those characters
 *    are appended, starting at the original terminating null of strDest, and a
 *    new terminating null is appended; otherwise, strDest[0] is set to the null
 *    character.
 *
 * <INPUT PARAMETERS>
 *    strDest                  Null-terminated destination string.
 *    destMax                  Size of the destination buffer.
 *    strSrc                   Null-terminated source string.
 *    count                    Number of character to append, or truncate.
 *
 * <OUTPUT PARAMETERS>
 *    strDest                  is updated
 *
 * <RETURN VALUE>
 *    EOK                      Successful operation
 *    EINVAL                   strDest not terminated or NULL, or strSrc is NULL
 *    ERANGE                   destMax is 0, too big or too small, count > 
 *                             SECUREC_STRING_MAX_LEN
 *    EOVERLAP_AND_RESET       dest buffer and source buffer are overlapped
 ********************************************************************************
*/
errno_t strncat_s(char* strDest, size_t destMax, const char* strSrc, size_t count)
{
    char* pHeader = strDest;
    size_t availableSize = destMax;
    IN_REGISTER const char* overlapGuard = NULL;

    if (destMax == 0 || destMax > SECUREC_STRING_MAX_LEN )
    {
        SECUREC_ERROR_INVALID_RANGE("strncat_s");
        return ERANGE;
    }

    if (strDest == NULL || strSrc == NULL)
    {
        SECUREC_ERROR_INVALID_PARAMTER("strncat_s");
        if (strDest != NULL)
        {
            pHeader[0] = '\0';
            return EINVAL_AND_RESET;
        }
        return EINVAL;
    }
#ifdef  COMPATIBLE_WIN_FORMAT
    if (count > SECUREC_STRING_MAX_LEN && count !=(size_t)-1)
#else
    if (count > SECUREC_STRING_MAX_LEN)
#endif
    {
        pHeader[0] = '\0';
        SECUREC_ERROR_INVALID_RANGE("strncat_s");
        return ERANGE_AND_RESET;
    }

    if (strDest < strSrc)
    {
        overlapGuard = strSrc;
        while (availableSize > 0 && *strDest != '\0')
        {
            if (strDest == overlapGuard)
            {
                pHeader[0] = '\0';
                SECUREC_ERROR_BUFFER_OVERLAP("strncat_s");
                return EOVERLAP_AND_RESET;
            }
            strDest++;
            availableSize--;
        }

        /* strDestination unterminated, return error. */
        if (availableSize == 0)
        {
            pHeader[0] = '\0';
            SECUREC_ERROR_INVALID_PARAMTER("strncat_s");
            return EINVAL_AND_RESET;
        }
        while (count > 0 && (*strDest++ = *strSrc++) != '\0' && --availableSize > 0)
        {
            if ( strDest == overlapGuard)
            {
                pHeader[0] = '\0';
                SECUREC_ERROR_BUFFER_OVERLAP("strncat_s");
                return EOVERLAP_AND_RESET;
            }
            count--;
        }
    }
    else
    {
        overlapGuard = strDest;
        while (availableSize > 0 && *strDest != '\0')
        {
            /*seek to string end, and no need to check overlap*/
            strDest++;
            availableSize--;
        }

        /* strDest unterminated, return error. */
        if (availableSize == 0)
        {
            pHeader[0] = '\0';
            SECUREC_ERROR_INVALID_PARAMTER("strncat_s");
            return EINVAL_AND_RESET;
        }
        while (count > 0 && (*strDest++ = *strSrc++) != '\0' && --availableSize > 0)
        {
            if ( strSrc == overlapGuard)
            {
                pHeader[0] = '\0';
                SECUREC_ERROR_BUFFER_OVERLAP("strncat_s");
                return EOVERLAP_AND_RESET;
            }
            count--;
        }
    }
    if (count == 0)
    {
        *strDest = 0; /* add terminator to strDest*/
    }

    /* strDest have not enough space,return error */
    if (availableSize == 0)
    {
        pHeader[0] = '\0';
        SECUREC_ERROR_INVALID_RANGE("strncat_s");
        return ERANGE_AND_RESET;
    }
    return EOK;
}

